/*
 * Copyright 2009 SHOP.COM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shop.opensocial.mediated.util;

import com.shop.opensocial.mediated.types.CollectionOptionsMediator;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;

/**
 * Some utilities for applying collection options
 *
 * @author Jordan Zimmerman
 */
public class CollectionOptionsUtils
{
	/**
	 * Apply all collection option settings (if any) using an internal Comparator that compares based on {@link String#valueOf(Object)}
	 *
	 * @param given the list to process
	 * @param collectionOptions collection options or null
	 * @return a new list with collection options applied
	 */
	public static<T> List<T> 	applyAll(List<T> given, CollectionOptionsMediator collectionOptions)
	{
		return applyAll(given, collectionOptions, null);
	}

	/**
	 * Apply all collection option settings (if any) using the given Comparator
	 *
	 * @param given the list to process
	 * @param collectionOptions collection options or null
	 * @param comparator comparison functor to use or null for default
	 * @return a new list with collection options applied
	 */
	public static<T> List<T> 	applyAll(List<T> given, CollectionOptionsMediator collectionOptions, Comparator<T> comparator)
	{
		List<T>		result = applyFilter(given, collectionOptions);
		result = applyFirstMax(result, collectionOptions);
		result = applySort(result, collectionOptions, comparator);

		return result;
	}

	/**
	 * Apply the first/max portion of the collection options (if any)
	 *
	 * @param given the list to process
	 * @param collectionOptions collection options or null
	 * @return a new list with collection options applied
	 */
	public static<T> List<T> 	applyFirstMax(List<T> given, CollectionOptionsMediator collectionOptions)
	{
		List<T>		result = null;
		if ( collectionOptions != null )
		{
			int 		totalSize = given.size();
			int 		last = collectionOptions.getFirst() + collectionOptions.getMax();
			if ( totalSize > 0 )
			{
				result = given.subList(Math.min(collectionOptions.getFirst(), totalSize), Math.min(last, totalSize));
			}
		}

		if ( result == null )
		{
			result = new ArrayList<T>(given);
		}

		return result;
	}

	/**
	 * Apply the sort portion of the collection options (if any) using an internal Comparator that compares based on {@link String#valueOf(Object)}
	 *
	 * @param given the list to process
	 * @param collectionOptions collection options or null
	 * @return a new list with collection options applied
	 */
	public static<T> List<T> 	applySort(List<T> given, CollectionOptionsMediator collectionOptions)
	{
		return applySort(given, collectionOptions, null);
	}

	/**
	 * Apply the sort portion of the collection option settings (if any) using the given Comparator
	 *
	 * @param given the list to process
	 * @param collectionOptions collection options or null
	 * @param comparator comparison functor to use or null for default
	 * @return a new list with collection options applied
	 */
	public static<T> List<T> 	applySort(List<T> given, CollectionOptionsMediator collectionOptions, Comparator<T> comparator)
	{
		List<T>			result = null;

		if ( (collectionOptions != null) && (collectionOptions.getSortOrder() != null) )
		{
			if ( comparator == null )
			{
				comparator = new StringComparator<T>();
			}

			result = new ArrayList<T>(given);
			switch ( collectionOptions.getSortOrder() )
			{
				case ASCENDING:
				{
					Collections.sort(result, comparator);
					break;
				}

				case DESCENDING:
				{
					Collections.sort(result, Collections.reverseOrder(comparator));
					break;
				}
			}
		}

		if ( result == null )
		{
			result = new ArrayList<T>(given);
		}
		
		return result;
	}

	/**
	 * Apply the filter portion of the collection options (if any)
	 *
	 * @param given the list to process
	 * @param collectionOptions collection options or null
	 * @return a new list with collection options applied
	 */
	public static<T> List<T> 	applyFilter(List<T> given, CollectionOptionsMediator collectionOptions)
	{
		List<T>			result = null;

		if ( (collectionOptions != null) && (collectionOptions.getFilterOperation() != null) )
		{
			result = new ArrayList<T>();
			for ( T v : given )
			{
				String		strValue = String.valueOf(v);
				switch ( collectionOptions.getFilterOperation() )
				{
				case CONTAINS:
				{
					if ( strValue.contains(collectionOptions.getFilterValue()) )
					{
						result.add(v);
					}
					break;
				}

				case EQUALS:
				{
					if ( strValue.equals(collectionOptions.getFilterValue()) )
					{
						result.add(v);
					}
					break;
				}

				case PRESENT:
				{
					if ( v != null )
					{
						result.add(v);
					}
					break;
				}

				case STARTSWITH:
				{
					if ( strValue.startsWith(collectionOptions.getFilterValue()) )
					{
						result.add(v);
					}
					break;
				}
				}
			}
		}

		if ( result == null )
		{
			result = new ArrayList<T>(given);
		}
		
		return result;
	}

	private static class StringComparator<T> implements Comparator<T>
	{
		@Override
		public int compare(T o1, T o2)
		{
			return String.valueOf(o1).compareTo(String.valueOf(o2));
		}
	}

	private CollectionOptionsUtils()
	{
	}
}
